package com.gitee.wsl.compose.use.coroutines

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import com.gitee.wsl.compose.state.S
import com.gitee.wsl.compose.state.S.state
import com.gitee.wsl.compose.state.ext.useLatestState
import com.gitee.wsl.coroutines.strategy.Debounce
import com.gitee.wsl.coroutines.strategy.DebounceOptions
import com.gitee.wsl.ext.reflect.VoidFunction
import com.gitee.wsl.ext.reflect.invoke

/** 使用 [useDebounceFn] 实现的 */
@Composable
fun <S> useDebounce(
    value: S,
    options: DebounceOptions = DebounceOptions.instance,
): S {
    val (debounced, setDebounced) = state(value)
    // 最简单的创建 noop 函数的方法就是使用 匿名函数 lambda。
    val debouncedSet = useDebounceFn(fn = {
        setDebounced(value)
    }, options)
    LaunchedEffect(key1 = value, block = {
        // 外部状态变更时，调用debounced后的setState函数
        debouncedSet.invoke()
    })
    // 对外只暴露状态的值，避免外部修改状态。
    return debounced
}

/**
 * 需要注意：[Debounce] 不返回计算结果，在 Compose 中我们无法使用 [Debounce] 透传出结算结果，应该使用状态，而非
 * [Debounce] 的返回值。 例如我们有一个计算函数，我们应该设置一个状态作为结果的保存。函数计算后的结果，通过调用对应的
 * `setState(state:T)` 函数来传递。保证结算结果（状态）与计算解耦。 这样我们的[Debounce] 就可以无缝接入。
 */
@Composable
fun useDebounceFn(
    fn: VoidFunction,
    options: DebounceOptions = DebounceOptions.instance,
): VoidFunction {
    val latestFn by S.useLatestState(fn)
    val scope = rememberCoroutineScope()
    val debounced = remember {
        Debounce(latestFn, scope, options)
    }.apply { this.fn = latestFn }
    return debounced
}


@Composable
fun useDebounceEffect(
    vararg keys: Any?,
    options: DebounceOptions = DebounceOptions.instance,
    block: () -> Unit,
) {
    val debouncedBlock = useDebounceFn(fn = { block() }, options)
    LaunchedEffect(*keys) {
        debouncedBlock()
    }
}
